#pragma once
#include <pthread.h>
#include <iostream>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <errno.h>
#include <string>
#include <sys/wait.h>
#include <signal.h>
#include <functional>

#include "Mutex.hpp"
#include "Cond.hpp"
#include "Thread.hpp"
#include "ThreadPool.hpp"
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"
using namespace LogModule;

static const uint16_t gport = 8081;
#define backlog 8
class tcpServer
{
    using task_t = std::function<void()>;
    using handler_t = std::function<std::string(std::string&)>;
private:
    uint16_t _port;
    bool _isRuinning = false;
    int _listenSockfd; // 监听套接字
    handler_t _handler; // 处理上层任务的接口
public:
    tcpServer(handler_t handler, int port = gport) :
     _port(gport), _handler(handler)
    {
    }
    ~tcpServer()
    {
    }
    void initServer()
    {
        // zdl:: 1. 创建 tcpsocket
        _listenSockfd = ::socket(AF_INET, SOCK_STREAM, 0); // tcp socket
        if (_listenSockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            Die(SOCKET_ERR);
            return;
        }
        LOG(LogLevel::INFO) << "socket create success, _socket: " << _listenSockfd;
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = ::htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        // zdl:: 2. bind
        int n = ::bind(_listenSockfd, CONV(&local), sizeof local);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            Die(BIND_ERR);
        }
        // zdl:: 3. tcp 面向连接， 就要求等待被链接
        // zdl :: tcp 需要将我们的 socket设置为监听状态
        n = ::listen(_listenSockfd, backlog);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success, sockfd: " << _listenSockfd;
        ::signal(SIGCHLD, SIG_IGN); // zdl:: 此时子进程退出的时候就会自动回收相应的资源，不用再wait
    }

    void start()
    {
        _isRuinning = true;

        while (_isRuinning)
        {
            // zdl:: 不能直接获取数据
            // 1. 获取新连接
            struct sockaddr_in peer;
            socklen_t peerLen = sizeof(peer); // ! 这个地方一定要注意
            LOG(LogLevel::DEBUG) << "accepting ...";
            int sockfd = ::accept(_listenSockfd, CONV(&peer), &peerLen);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error" << strerror(errno);
                continue;
            }

            // zdl:: 获取链接成功了
            LOG(LogLevel::INFO) << "accept success, sockfd: " << sockfd;
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "client info " << addr.Addr();
            // version -0
            // handlerRequest(sockfd);

            // // version -1 多进程版本
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // child
            //     // 问题1 ：子进程和父进程的文件描述符表有两张
            //     // 关闭不需要的文件描述符
            //     ::close(_listenSockfd);
            //     handlerRequest(sockfd);
            //     exit(0);
            // }
            // ::close(sockfd);
            // // ?? 此时使用 signal 直接回收进程，不需要再wait
            // // int rid = ::waitpid(id, nullptr, 0);
            // // if (rid < 0)
            // // {
            // //     LOG(LogLevel::WARNING) << "wait Error";
            // // }

            // // version 2
            // pthread_t tid;
            // // pthread_create(&tid, nullptr, handlerRequest, &sockfd); // ! 不能这样做
            // threadData *data = new threadData;
            // data->self = this;
            // data->sockfd = sockfd;
            // pthread_create(&tid, nullptr, threadEntry, data);

            // version 3 线程池版本  线程池版本适合处理短任务
            task_t f = std::bind(&tcpServer::handlerRequest,
                                 this, sockfd); // 构建任务
            ThreadPoolModule::ThreadPool<task_t>::
                getInstance()
                    ->Equeue(f);
            
        }
        _isRuinning = false;
    }
    void stop()
    {
        _isRuinning = false;
    }

private:
    // void handlerRequest(int sockfd) // tcp也是全双工
    // {
    //     char inbuffer[1024];
    //     while (true)
    //     {
    //         LOG(LogLevel::INFO) << "handlerReuest, sockfd is " << sockfd;
    //         ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
    //         if (n > 0)
    //         {
    //             inbuffer[n] = 0;
    //             std::string echo_str = "server echo# ";

    //             echo_str += inbuffer;
    //             ::write(sockfd, echo_str.c_str(), echo_str.size());
    //             LOG(LogLevel::INFO) << echo_str;
    //         }
    //         else if (n == 0)
    //         {
    //             // read 如果返回值就是0 ，标识 client退出
    //             LOG(LogLevel::INFO) << "client quit, sockfd is " << sockfd;
    //             break;
    //         }
    //     }
    //     ::close(sockfd);
    // }
    
    // zdl:: version -2 多线程版本
    void handlerRequest(int &sockfd) // tcp也是全双工
    {
        // * 约定用户发给我的是一个完整的命令 string
        char inbuffer[1024];
        std::string package;
        while (true)
        {
            LOG(LogLevel::INFO) << "handlerReuest, sockfd is " << sockfd;
            // ssize_t n = read(sockfd, inbuffer, sizeof(inbuffer) - 1);
            ssize_t n = recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
            if (n > 0)
            {
                inbuffer[n] = 0;
                LOG(LogLevel::INFO) << "\n" << inbuffer;
                package += inbuffer;
                auto cmd_ret = _handler(package);
                if (cmd_ret.empty()) continue;
                ::send(sockfd, cmd_ret.c_str(), cmd_ret.size(), 0);
            }
            else if (n == 0)
            {
                // read 如果返回值就是0 ，标识 client退出
                LOG(LogLevel::INFO) << "client quit, sockfd is " << sockfd;
                break;
            }
        }
        ::close(sockfd);
    }
    // return nullptr;
    // zdl:: 要记住，在类里面的静态函数不能直接访问类里面的成员函数，此时就需要通过传参的方式来进行访问
    static void *threadEntry(void *args)
    {
        pthread_detach(pthread_self());
        threadData data = *static_cast<threadData *>(args);
        data.self->handlerRequest(data.sockfd);
        return nullptr;
    }
    struct threadData
    {
        int sockfd;
        tcpServer *self;
    };
};